import libtcodpy as libtcod

import data

def render_bar(panel, x, y, total_width, name, value, maximum, color, back_color):
    # render a bar (HP, experience, etc). first calculate the width of the bar
    bar_width = int(float(value) / maximum * total_width)
    # render the background first
    libtcod.console_set_background_color(panel, back_color)
    libtcod.console_rect(panel, x, y, total_width, 1, False)
    # now render the bar on top
    libtcod.console_set_background_color(panel, color)
    if 0 < bar_width <= total_width:
        libtcod.console_rect(panel, x, y, bar_width, 1, False)
        # finally, some centered text with the values
    libtcod.console_set_foreground_color(panel, libtcod.white)
    libtcod.console_print_center(panel, x + total_width / 2, y, libtcod.BKGND_NONE,
                                 name + ': ' + str(value) + '/' + str(maximum))

def redraw_fov(scene, player, console):
    """
    redraws the fov in a given scene
    """
    libtcod.map_compute_fov(scene.fov_map, player.location[0], player.location[1], data.SIGHT_RADIUS, True, libtcod.FOV_SHADOW)
    for y in range(data.MAP_HEIGHT):
        for x in range(data.MAP_WIDTH):
            visible = libtcod.map_is_in_fov(scene.fov_map, x, y)
            if visible:
                scene.explored[x][y] = 1
                effect = 'light'
            else:  # tile has been seen but is not currently in view
                effect = 'dark'
                # render all explored tiles
            if scene.explored[x][y]:
                tile = data.TERRAIN[scene.grid[x][y]]
                libtcod.console_set_foreground_color(console, tile[effect])
                libtcod.console_put_char(console, x, y, tile['character'], libtcod.BKGND_NONE)
            else:
                libtcod.console_put_char(console, x, y, ' ', libtcod.BKGND_NONE)

def draw_units(scene, console):
    """
    redraws the units from a scene onto the console
    """
    for item in scene.items:
        if libtcod.map_is_in_fov(scene.fov_map, item.location[0], item.location[1]):
            libtcod.console_set_foreground_color(console, item.color)
            libtcod.console_put_char(console, item.location[0], item.location[1], item.character, libtcod.BKGND_NONE)
 
    for unit in scene.units:
        if libtcod.map_is_in_fov(scene.fov_map, unit.location[0], unit.location[1]):
            libtcod.console_set_foreground_color(console, unit.color)
            libtcod.console_put_char(console, unit.location[0], unit.location[1], unit.character, libtcod.BKGND_NONE)

def refresh(console, panel):
    """
    refreshes the screen
    """
    libtcod.console_blit(console, 0, 0, data.MAP_WIDTH, data.MAP_HEIGHT, 0, 0, 0)
    libtcod.console_set_background_color(panel, libtcod.Color(0, 0, 0))
    libtcod.console_clear(panel)
    
def flush(panel, r_panel):
    """
    blits and flushes the panel
    """
    libtcod.console_blit(panel, 0, 0, data.SCREEN_WIDTH, data.PANEL_HEIGHT, 0, 0, data.SCREEN_HEIGHT - data.PANEL_HEIGHT)
    libtcod.console_blit(r_panel, 0, 0, data.LOADOUT_WIDTH, data.LOADOUT_HEIGHT, 0, data.SCREEN_WIDTH - data.LOADOUT_WIDTH, 0)
    libtcod.console_flush()

def draw_bars(panel, unit, x):
    """
    draws those weird stat bar things onto a panel at a given x coordinate
    """
    libtcod.console_set_foreground_color(panel, unit.color)
    libtcod.console_put_char(panel, x+data.BAR_WIDTH-1, 0, unit.character, libtcod.BKGND_NONE)
    libtcod.console_print_left(panel, x, 0, libtcod.BKGND_NONE, unit.name.capitalize())
    render_bar(panel, x, 1, data.BAR_WIDTH, 'STR',
               unit.stats['strength'], unit.base_stats['strength'],
               libtcod.red, libtcod.dark_red)
    render_bar(panel, x, 2, data.BAR_WIDTH, 'STA',
               unit.stats['stamina'], unit.base_stats['stamina'],
               libtcod.green, libtcod.dark_green)
    render_bar(panel, x, 3, data.BAR_WIDTH, 'FOC',
               unit.stats['focus'], unit.base_stats['focus'],
               libtcod.blue, libtcod.dark_blue)
    if unit.is_player:
        libtcod.console_print_left(panel, x, 5, libtcod.BKGND_NONE, 'EXP: {}'.format(unit.exp))
        libtcod.console_print_left(panel, x, 6, libtcod.BKGND_NONE, str(libtcod.sys_get_fps()))

def update_loadout(loadout, player):
    libtcod.console_set_foreground_color(loadout, libtcod.Color(255,255,115))
    y=1
    if libtcod.console_is_key_pressed(libtcod.KEY_ALT):
        move_type = 'run'
        libtcod.console_print_left(loadout, 0, 1, libtcod.BKGND_NONE, 'Run  /Smash')
    elif libtcod.console_is_key_pressed(libtcod.KEY_CONTROL):
        move_type = 'sneak'
        libtcod.console_print_left(loadout, 0, 1, libtcod.BKGND_NONE, 'Sneak/Stab')
    else:
        move_type = 'normal'
        libtcod.console_print_left(loadout, 0, 1, libtcod.BKGND_NONE, 'Walk /Hit')
    libtcod.console_print_left(loadout, 0, y+1, libtcod.BKGND_NONE, 'ACC/EVA  : {:c}{:c}{:c}{:c}{}/{}{:c}'.format(libtcod.COLCTRL_FORE_RGB,1,255,1,player.stats['stamina']+player.skill_ranks['accuracy']+data.BONUS_SKILLS[move_type]['accuracy'],player.stats['stamina']+player.skill_ranks['evasion']+data.BONUS_SKILLS[move_type]['evasion'],libtcod.COLCTRL_STOP))
    libtcod.console_print_left(loadout, 0, y+2, libtcod.BKGND_NONE, 'FOR/GRD  : {:c}{:c}{:c}{:c}{}/{}{:c}'.format(libtcod.COLCTRL_FORE_RGB,255,1,1,player.stats['strength']+player.skill_ranks['force']+data.BONUS_SKILLS[move_type]['force'],player.stats['strength']+player.skill_ranks['guard']+data.BONUS_SKILLS[move_type]['guard'],libtcod.COLCTRL_STOP))
    libtcod.console_print_left(loadout, 0, y+3, libtcod.BKGND_NONE, 'STL/DET  : {:c}{:c}{:c}{:c}{}/{}{:c}'.format(libtcod.COLCTRL_FORE_RGB,1,1,255,player.stats['focus']+player.skill_ranks['stealth']+data.BONUS_SKILLS[move_type]['stealth'],player.stats['focus']+player.skill_ranks['detection']+data.BONUS_SKILLS[move_type]['detection'],libtcod.COLCTRL_STOP))
    libtcod.console_print_left(loadout, 0, y+4, libtcod.BKGND_NONE, 'DMG/ARM  : {}/{}'.format(player.misc_stats['lethality'], player.misc_stats['resistance']))
    if move_type == 'run':
        libtcod.console_set_foreground_color(loadout, libtcod.red)
        libtcod.console_print_left(loadout, 0, y+5, libtcod.BKGND_NONE, '(costs 1 STA)')
    if move_type == 'sneak':
        libtcod.console_set_foreground_color(loadout, libtcod.red)
        libtcod.console_print_left(loadout, 0, y+5, libtcod.BKGND_NONE, '(depletes FOC)')
    

    libtcod.console_set_foreground_color(loadout, libtcod.Color(255,255,115))
    libtcod.console_print_left(loadout, 0, y+7, libtcod.BKGND_NONE, 'SPEED : {}/{}'.format(player.misc_stats['move_time']+data.MOVE_STATS[move_type]['move_time'], player.misc_stats['attack_time']+data.MOVE_STATS[move_type]['attack_time']))
    
    libtcod.console_print_left(loadout, 0, 10, libtcod.BKGND_NONE, 'EQUIPMENT:')
    y = 11
    for slot in data.EQUIPMENT_SLOTS:
        libtcod.console_print_left(loadout, 0, y, libtcod.BKGND_NONE, '{}: {}'.format(slot, player.equipment[slot]))
        y += 1

    y += 2
    libtcod.console_print_left(loadout, 0, y, libtcod.BKGND_NONE, 'INVENTORY:')
    y += 1
    for item in player.inventory:
        libtcod.console_print_left(loadout, 0, y, libtcod.BKGND_NONE, '{} {} ({} lb)'.format(item.quantity, item.name, item.weight*item.quantity))
        y += 1

    y = 30
    libtcod.console_print_left(loadout, 0, y, libtcod.BKGND_NONE, 'FLOOR:')
    for item in player.scene.items:
        if item.location == player.location:
            y += 1
            libtcod.console_print_left(loadout, 0, y, libtcod.BKGND_NONE, item.name)

def update_dirty(scene, console):
    """clear tiles marked for clearing"""
    for location in scene.dirty:
        if libtcod.map_is_in_fov(scene.fov_map, location[0], location[1]):
            tile = data.TERRAIN[scene.grid[location[0]][location[1]]]
            libtcod.console_set_foreground_color(console, tile['light'])
            libtcod.console_put_char(console, location[0], location[1], tile['character'], libtcod.BKGND_NONE)

def draw_stuff(console, panel, r_panel, scene, player, output, messages, old_messages, fov_redraw, timestamp):
    """
    handles all the drawing functions needed for 1 turn
    """
    if fov_redraw:
        fov_redraw = False
        redraw_fov(scene, player, console)
    else:
        update_dirty(scene, console)
    scene.dirty = set([])
    # draw traps, then items, then units
    draw_units(scene, console)
    libtcod.console_blit(console, 0, 0, data.MAP_WIDTH, data.MAP_HEIGHT, 0, 0, 0)
    refresh(console, panel)
    draw_bars(panel, player, 1)
    refresh(console, r_panel)
    update_loadout(r_panel, player)
    if player.target and player.target.alive:
        draw_bars(panel, player.target, 23)
    output_text(output, messages, old_messages, timestamp)
    if messages:
        y = len(messages) - 1
        while y >= 0:
            if messages[y][1] > timestamp - 5:
                libtcod.console_set_foreground_color(panel, libtcod.Color(100,100,100))
            if messages[y][1] > timestamp - 2:
                libtcod.console_set_foreground_color(panel, libtcod.Color(255,255,255))
            else:
                libtcod.console_set_foreground_color(panel, libtcod.Color(50,50,50))
            libtcod.console_print_left_rect(panel, data.MSG_X, len(messages)-1-y, data.MSG_WIDTH, data.MSG_HEIGHT, libtcod.BKGND_NONE, messages[y][0])
            y -= len(messages[y][0])/data.MSG_WIDTH + 1
    flush(panel, r_panel)
    libtcod.console_set_foreground_color(panel, libtcod.Color(255,255,255))
    libtcod.console_print_left(panel, 1, 1, libtcod.BKGND_NONE, str(fov_redraw))
    return fov_redraw

def output_text(text, messages, old_messages, timestamp=0):
    if text:
        for line in text:
            if line[0] == 'attack':
                line = parse_attack_info(line[1])
            if line[0] == 'instant':
                messages.append([line[1], timestamp-1])
            else:
                messages.append([line, timestamp])
        while len(messages) > data.MSG_HEIGHT:
            old_messages.append(messages.pop(0))

    return messages

def parse_attack_info(info):
    attacker = info['attacker']
    target = info['target']
    connected = info['hit'] >= 0 or info['pierced']
    if connected:
        verb = data.VERBS[info['move_type']][info['player']]
        if info['pierced'] and info['amount'] > 0:
            amount = ' for {:c}{:c}{:c}{:c}{}{:c}!'.format(libtcod.COLCTRL_FORE_RGB,255,1,1, info['amount'], libtcod.COLCTRL_STOP)
        elif info['hit'] >= 0 and info['amount'] > 0:
            amount = ' for {:c}{:c}{:c}{:c}{}{:c}.'.format(libtcod.COLCTRL_FORE_RGB,1,255,1, info['amount'], libtcod.COLCTRL_STOP)
        elif info['amount'] <= 0:
            amount = ' to no effect...'
    else: 
        verb = data.VERBS['miss'][info['player']]
        amount = '.'

    return '{} {} {}{}'.format(attacker, verb, target, amount)

def menu(console, header, options, width, shop=False, confirm_msg='', description_width=0):  # create a popup menu that returns chosen index
    # calculate total height for the header (after auto-wrap) and one line per option
    '''
    if shop:
        to be passed an array of [item][return value][description][requirement_string]
    else:
        to be passed an array of [item description][return value]
    '''
    select_index = 0
    if shop and not description_width:
        description_width = 45
    header_height = libtcod.console_height_left_rect(console, 0, 0, width, data.SCREEN_HEIGHT, header)
    if header == '':
        header_height = 0
    selector = '*'
    lengths = []
    height = header_height
    for option in options:
        lengths.append(libtcod.console_height_left_rect(console, 5, 0, width-description_width, data.SCREEN_HEIGHT, option[0]))
    height += sum(lengths)
    if shop:
        height = max(height, header_height + 10)
    
    # create an off-screen console that represents the menu's window
    window = libtcod.console_new(width, height)
    libtcod.console_set_background_color(window, libtcod.Color(20,20,20))

    selection = None
    while not selection:
        # print all the options
        libtcod.console_clear(window)
        libtcod.console_set_foreground_color(window, libtcod.yellow)
        libtcod.console_print_left_rect(window, 0, 0, width, height, libtcod.BKGND_NONE, header)
        letter_index = ord('a')
        y = header_height
        libtcod.console_set_foreground_color(window, libtcod.white)
        for option in options:
            if options.index(option) == select_index:
                libtcod.console_set_background_color(window, libtcod.grey)
                libtcod.console_print_left(window, 0, y, libtcod.BKGND_NONE, selector)
                libtcod.console_print_left_rect(window, 5, y, width-description_width, height, libtcod.BKGND_SET, '{}'.format(option[0]))
                libtcod.console_set_background_color(window, libtcod.Color(20,20,20))
            else:
                libtcod.console_print_left(window, 0, y, libtcod.BKGND_NONE, ' ')
                libtcod.console_print_left_rect(window, 5, y, width-description_width, height, libtcod.BKGND_SET, '{}'.format(option[0]))
            libtcod.console_print_left_rect(window, 1, y, width-description_width, height, libtcod.BKGND_NONE, 
                '{:c}{:c}{:c}{:c}('.format(libtcod.COLCTRL_FORE_RGB, 255, 255, 115
                        ) + chr(letter_index) + '){:c}'.format(libtcod.COLCTRL_STOP))

            y += lengths[options.index(option)]
            letter_index += 1
            if letter_index == 123:
                letter_index -= 58

        if shop:
            libtcod.console_print_left_rect(window, width-description_width, header_height, description_width, 5, libtcod.BKGND_NONE, options[select_index][2])
            libtcod.console_print_left_rect(window, width-description_width, header_height+6, description_width, 5, libtcod.BKGND_NONE, options[select_index][3])
            print header_height, height
            libtcod.console_print_left(window, width-description_width, height-1, libtcod.BKGND_SET, confirm_msg)

        # blit the contents of "window" to the root console
        x = 0
        y = data.SCREEN_HEIGHT / 2 - height / 2
        libtcod.console_blit(window, 0, 0, width, height, 0, x, y, 1.0, 1)

        # present the root console to the player and wait for a key-press
        libtcod.console_flush()
        key = libtcod.console_wait_for_keypress(True)

        if key.vk == libtcod.KEY_SHIFT:
            key = libtcod.console_wait_for_keypress(False)

        if key.vk == libtcod.KEY_ENTER and key.lalt:  # (special case) Alt+Enter: toggle fullscreen
            libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())

        if (key.vk == libtcod.KEY_DOWN or key.vk == libtcod.KEY_KP2) and select_index < len(options) - 1:
            select_index += 1

        if (key.vk == libtcod.KEY_UP or key.vk == libtcod.KEY_KP8) and select_index > 0:
            select_index -= 1

        if key.vk in [libtcod.KEY_ENTER, libtcod.KEY_RIGHT, libtcod.KEY_KP6]:
            selection = options[select_index][1]
        # convert the ASCII code to an index; if it corresponds to an option, return it
        if key.c < ord('a'):
            index = key.c - ord('a') + 58
        else:
            index = key.c - ord('a')

        if 0 <= index < len(options):
            selection = options[index][1]

        if key.vk in [libtcod.KEY_ESCAPE, libtcod.KEY_LEFT, libtcod.KEY_KP4]:
            break
    libtcod.console_blit(console, 0, 0, data.MAP_WIDTH, data.MAP_HEIGHT, 0, 0, 0)
    return selection
    #return None